package main

import (
	"bytes"
	"container/list"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
)

var (
	pool       = NewBufferPool(1024)
	config     *Config
	quits      list.List
	formatters = make(map[string]IFormatter)
	firewalls  = make(map[string]IDbFirewall)
)

type IDbFirewall func(server net.Conn, client net.Conn, block []*FirewallBlock, exit chan bool)

const (
	chunkSize = 1024
)

func StartProxy() error {
	return startProxy()
}

func ReloadProxy() (err error) {
	err = StopProxy()
	if err != nil {
		return
	}
	//重新读取配置文件
	config.LoadConf(configPathFlag)
	err = startProxy()
	if err != nil {
		return
	}
	log.Println("gate has been reloaded")
	return
}

func StopProxy() error {
	for e := quits.Front(); e != nil; e = e.Next() {
		quit := e.Value.(chan bool)
		quit <- true
	}
	quits.Init()
	return nil
}

func startProxy() error {
	log.Printf("%+v", config.Servers)
	for _, server := range config.Servers {
		if len(server.ProxyPass) > 0 && server.Listen > 0 {
			//tcp代理
			serverUrl := fmt.Sprintf(":%d", server.Listen)
			clientUrl := server.ProxyPass
			go proxyTcp(server, serverUrl, clientUrl)
		} else if server.Penetration != nil {
			//内网穿透
			limit := 100
			for i := server.Penetration.StartPort; i <= server.Penetration.EndPort; i++ {
				limit--
				if limit == 0 {
					log.Println("cannot penetration more than 100 ports")
					break
				}
				var port uint16
				if server.Penetration.BasePort > 0 {
					port = server.Penetration.BasePort + i - server.Penetration.StartPort
				} else {
					port = i
				}
				go proxyTcp(server, fmt.Sprintf(":%d", port), fmt.Sprintf("%s:%d", server.Penetration.Url, i))
			}
		} else if server.Listen > 0 {
			//http代理
			go proxyHttp(server)
		}
	}
	return nil
}

func proxyTcp(server *ServerBlock, serverUrl string, clientUrl string) {
	listener, err := net.Listen("tcp", serverUrl)
	if err != nil {
		return
	}

	log.Printf("server is listening on %s to provide %s tcp service\r\n", serverUrl, clientUrl)

	quit := make(chan bool)
	quits.PushBack(quit)

	//Listen
	go func() {
		select {
		case <-quit:
			_ = listener.Close()
		}
	}()

	for {
		conn, err := listener.Accept() //用conn接收链接
		if err != nil {
			log.Println(err)
			select {
			case <-quit:
				return
			default:
				continue
			}
		}
		go handleSocket(server, conn, clientUrl)
	}

}

func handleSocket(serverBlock *ServerBlock, server net.Conn, url string) {
	defer server.Close()
	client, err := net.Dial("tcp", url)
	if err != nil {
		log.Println(err)
		return
	}
	defer client.Close()

	chanExit := make(chan bool)

	if len(serverBlock.FirewallType) == 0 {
		go func() {
			buffer := pool.Get(1024)
			defer pool.Put(buffer)
			_, _ = io.CopyBuffer(client, server, buffer)
			chanExit <- true
		}()
		go func() {
			buffer := pool.Get(1024)
			defer pool.Put(buffer)
			_, _ = io.CopyBuffer(server, client, buffer)
			chanExit <- true
		}()

	} else if dbfire, ok := firewalls[serverBlock.FirewallType]; ok {
		go dbfire(server, client, serverBlock.Firewalls, chanExit)
	}

	<-chanExit

	//for {
	//	time.Sleep(time.Duration(1000) * time.Second)
	//}
	//for{
	//	if err != nil {
	//		break
	//	}
	//	log.Println(string(ebc2asc(bs)))
	//}

	//return

}

func readAndWrite(server net.Conn, client net.Conn) (bs []byte, err error) {
	//读取
	bs, err = readPacket(server)
	if err != nil {
		return
	}
	_, err = client.Write(bs)
	if err != nil {
		return
	}
	return
}

func readPacket(reader io.Reader) ([]byte, error) {
	data := pool.Get(4096)
	defer pool.Put(data)
	buf := bytes.Buffer{}
	for {
		n, err := reader.Read(data)
		if err != nil {
			return nil, err
		}
		buf.Write(data[:n])
		if n != len(data) {
			break
		}
	}
	return buf.Bytes(), nil
}

func proxyHttp(server *ServerBlock) {
	srv := &http.Server{
		Addr:    fmt.Sprintf(":%d", server.Listen),
		Handler: http.HandlerFunc(NewHttpHandler(server)),
	}

	log.Printf("server is listening on :%d to provide http service\r\n", server.Listen)

	quit := make(chan bool)
	quits.PushBack(quit)

	go func() {
		select {
		case <-quit:
			_ = srv.Shutdown(nil)
		}
	}()

	_ = srv.ListenAndServe()
}

func RegisterFormatter(name string, formatter IFormatter) {
	//this.formatters[name] = formatter
}

func getFormatter(name string) IFormatter {
	//if formatter, ok := this.formatters[name]; ok {
	//	return formatter
	//}
	return nil
}

func RegisterDbFirewall(name string, firewall IDbFirewall) {
	firewalls[name] = firewall
}

func GetDbFirewall(name string) (IDbFirewall, bool) {
	if formatter, ok := firewalls[name]; ok {
		return formatter, true
	}
	return nil, false
}
